#include <bits/stdc++.h>
using namespace std;

/*
1625. 执行操作后字典序最小的字符串
已解答
中等
相关标签
premium lock icon
相关企业
提示
给你一个字符串 s 以及两个整数 a 和 b 。其中，字符串 s 的长度为偶数，且仅由数字 0 到 9 组成。

你可以在 s 上按任意顺序多次执行下面两个操作之一：

累加：将  a 加到 s 中所有下标为奇数的元素上（下标从 0 开始）。数字一旦超过 9 就会变成 0，如此循环往复。例如，s = "3456" 且 a = 5，则执行此操作后 s 变成 "3951"。
轮转：将 s 向右轮转 b 位。例如，s = "3456" 且 b = 1，则执行此操作后 s 变成 "6345"。
请你返回在 s 上执行上述操作任意次后可以得到的 字典序最小 的字符串。

如果两个字符串长度相同，那么字符串 a 字典序比字符串 b 小可以这样定义：在 a 和 b 出现不同的第一个位置上，字符串 a 中的字符出现在字母表中的时间早于 b 中的对应字符。例如，"0158” 字典序比 "0190" 小，因为不同的第一个位置是在第三个字符，显然 '5' 出现在 '9' 之前。

 

示例 1：

输入：s = "5525", a = 9, b = 2
输出："2050"
解释：执行操作如下：
初态："5525"
轮转："2555"
累加："2454"
累加："2353"
轮转："5323"
累加："5222"
累加："5121"
轮转："2151"
累加："2050"​​​​​
无法获得字典序小于 "2050" 的字符串。
示例 2：

输入：s = "74", a = 5, b = 1
输出："24"
解释：执行操作如下：
初态："74"
轮转："47"
累加："42"
轮转："24"​​​​​
无法获得字典序小于 "24" 的字符串。
示例 3：

输入：s = "0011", a = 4, b = 2
输出："0011"
解释：无法获得字典序小于 "0011" 的字符串。
 

提示：

2 <= s.length <= 100
s.length 是偶数
s 仅由数字 0 到 9 组成
1 <= a <= 9
1 <= b <= s.length - 1
*/

// 法一
class Solution {
public:
    string findLexSmallestString(string s, int a, int b) {
        int n = s.size();
        unordered_set<string> visited; // 记录已访问的状态，避免重复
        queue<string> q;              // BFS队列，存储待处理的状态
        string min_str = s;           // 初始化最小字符串为原始串
        // 初始状态入队
        q.push(s);
        visited.insert(s);
        while (!q.empty()) {
            string curr = q.front();
            q.pop();
            // 更新全局最小字符串
            if (curr < min_str)  min_str = curr;
            // 生成操作1：累加（所有奇数下标加a mod10）
            string add_str = curr;
            for (int i = 1; i < n; i += 2) {
                int num = (add_str[i] - '0' + a) % 10;
                add_str[i] = num + '0';
            }
            // 未访问过则入队
            if (!visited.count(add_str)) {
                visited.insert(add_str);
                q.push(add_str);
            }
            // 生成操作2：向右轮转b位
            string rot_str = rotateStr(curr, b, n);
            // 未访问过则入队
            if (!visited.count(rot_str)) {
                visited.insert(rot_str);
                q.push(rot_str);
            }
        }
        return min_str;
    }
    // helper
    string rotateStr(const string& s, int b, int n) {
        b %= n; // 轮转n位等同于不轮转，取模简化
        if (b <= 0) return s;
        // 后b位拼接到前面，即完成向右轮转b位
        return s.substr(n - b, b) + s.substr(0, n - b);
    }
};

// 法二
class Solution {
public:
    string findLexSmallestString(string s, int a, int b) {
        int n = s.size();
        string res = s;          // 初始化结果为原始字符串（初始最小候选）
        s = s + s;               // 拼接s+s，方便通过子串直接获取所有轮转结果（避免重复拼接逻辑）
        int g = gcd(b, n);       // 计算b与n的最大公约数，确定轮转周期：每g步取一次起始位置，覆盖所有不同轮转状态
        
        // 累加优化函数：对字符串t中，从start开始的所有同奇偶位（i+=2）做累加，使该组位置最小
        auto add = [&](string& t, int start) {
            int minVal = 10, times = 0;  // minVal：目标最小数字；times：需要累加的次数（0-9）
            // 遍历10种累加可能（因模10循环），找到让start位置数字最小的累加次数
            for (int i = 0; i < 10; i++) {
                int added = ((t[start] - '0') + i * a) % 10;
                if (added < minVal) {
                    minVal = added;
                    times = i;
                }
            }
            // 用找到的最优次数，更新所有与start同奇偶的位置（i+=2）
            for (int i = start; i < n; i += 2) {
                t[i] = '0' + ((t[i] - '0') + times * a) % 10;
            }
        };

        // 遍历所有不同的轮转起始位置（每g步取一次，覆盖所有轮转状态）
        for (int i = 0; i < n; i += g) {
            string t = s.substr(i, n);  // 取轮转后的子串（从i开始，长度n）
            add(t, 1);                  // 第一步：优化奇数位（start=1），使所有奇数位最小
            if (b % 2) {                // 若b是奇数：轮转后奇偶位会互换，偶数位也能被优化，需再处理偶数位
                add(t, 0);              // 第二步：优化偶数位（start=0），使所有偶数位最小
            }
            res = min(res, t);          // 更新全局最小字符串
        }
        return res;
    }
};